// 导入类型定义
import { ErrorInfo, SDKConfig } from '../types'; // 导入错误信息和SDK配置类型
import {
  getCurrentUrl, // 获取当前页面URL
  getUserAgent, // 获取用户代理
  formatErrorStack, // 格式化错误堆栈
  shouldSample // 判断是否采样
} from '../utils'; // 导入工具函数

// 错误监控器类，负责采集和处理各种错误
export class ErrorMonitor {
  private config: SDKConfig; // SDK配置
  private errorHandler: (error: ErrorInfo) => void; // 错误处理回调

  // 构造函数，传入配置和回调
  constructor(config: SDKConfig, errorHandler: (error: ErrorInfo) => void) {
    this.config = config; // 保存配置
    this.errorHandler = errorHandler; // 保存回调
  }

  // 初始化错误监控
  public init(): void {
    this.setupJsErrorHandler(); // 监听JS错误
    this.setupPromiseErrorHandler(); // 监听Promise错误
    this.setupResourceErrorHandler(); // 监听资源加载错误
    this.setupHttpErrorHandler(); // 监听HTTP请求错误
    if (this.config.enableConsole) {
      this.setupConsoleErrorHandler(); // 监听控制台错误
    }
  }

  // 设置JS错误处理器
  private setupJsErrorHandler(): void {
    window.addEventListener('error', (event) => {
      if (!shouldSample(this.config.sampleRate)) {
        return; // 不采样则返回
      }
      const errorInfo: ErrorInfo = {
        type: 'js', // 错误类型
        message: event.message || 'Unknown JavaScript error', // 错误信息
        stack: event.error?.stack || '', // 堆栈信息
        filename: event.filename || '', // 文件名
        lineno: event.lineno || 0, // 行号
        colno: event.colno || 0, // 列号
        timestamp: Date.now(), // 时间戳
        url: getCurrentUrl(), // 当前URL
        userAgent: getUserAgent(), // 用户代理
        extra: {
          error: event.error // 原始错误对象
        }
      };
      this.errorHandler(errorInfo); // 上报错误
    }, true); // 捕获阶段
  }

  // 设置Promise错误处理器
  private setupPromiseErrorHandler(): void {
    window.addEventListener('unhandledrejection', (event) => {
      if (!shouldSample(this.config.sampleRate)) {
        return; // 不采样则返回
      }
      const errorInfo: ErrorInfo = {
        type: 'promise', // 错误类型
        message: event.reason?.message || event.reason || 'Unhandled Promise rejection', // 错误信息
        stack: event.reason?.stack || '', // 堆栈信息
        timestamp: Date.now(), // 时间戳
        url: getCurrentUrl(), // 当前URL
        userAgent: getUserAgent(), // 用户代理
        extra: {
          reason: event.reason // 原始reason
        }
      };
      this.errorHandler(errorInfo); // 上报错误
    });
  }

  // 设置资源加载错误处理器
  private setupResourceErrorHandler(): void {
    window.addEventListener('error', (event) => {
      if (!shouldSample(this.config.sampleRate)) {
        return; // 不采样则返回
      }
      // 只处理资源加载错误
      if (event.target && event.target !== window) {
        const target = event.target as HTMLElement; // 资源元素
        const errorInfo: ErrorInfo = {
          type: 'resource', // 错误类型
          message: `Failed to load resource: ${target.tagName}`, // 错误信息
          timestamp: Date.now(), // 时间戳
          url: getCurrentUrl(), // 当前URL
          userAgent: getUserAgent(), // 用户代理
          extra: {
            tagName: target.tagName, // 标签名
            src: (target as HTMLImageElement).src || (target as HTMLScriptElement).src || (target as HTMLLinkElement).href, // 资源地址
            target: target // 原始元素
          }
        };
        this.errorHandler(errorInfo); // 上报错误
      }
    }, true); // 捕获阶段
  }

  // 设置HTTP请求错误处理器
  private setupHttpErrorHandler(): void {
    // 拦截XMLHttpRequest
    const originalXHROpen = XMLHttpRequest.prototype.open; // 保存原始open方法
    const originalXHRSend = XMLHttpRequest.prototype.send; // 保存原始send方法
    const self = this; // 保存this指针
    XMLHttpRequest.prototype.open = function(method: string, url: string, async: boolean = true, username?: string | null, password?: string | null) {
      (this as any)._xhrMethod = method; // 记录请求方法
      (this as any)._xhrUrl = url; // 记录请求URL
      return originalXHROpen.call(this, method, url, async, username, password); // 调用原始方法
    };
    XMLHttpRequest.prototype.send = function(body?: Document | XMLHttpRequestBodyInit | null) {
      this.addEventListener('error', (event) => {
        if (!shouldSample(self.config.sampleRate)) {
          return; // 不采样则返回
        }
        const errorInfo: ErrorInfo = {
          type: 'http', // 错误类型
          message: `XHR Error: ${(this as any)._xhrMethod} ${(this as any)._xhrUrl}`, // 错误信息
          timestamp: Date.now(), // 时间戳
          url: getCurrentUrl(), // 当前URL
          userAgent: getUserAgent(), // 用户代理
          extra: {
            method: (this as any)._xhrMethod, // 请求方法
            url: (this as any)._xhrUrl, // 请求URL
            status: this.status, // 状态码
            statusText: this.statusText // 状态文本
          }
        };
        self.errorHandler(errorInfo); // 上报错误
      });
      return originalXHRSend.call(this, body); // 调用原始send
    };
    // 拦截Fetch API
    const originalFetch = window.fetch; // 保存原始fetch
    window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
      try {
        const response = await originalFetch(input, init); // 调用原始fetch
        if (!response.ok && shouldSample(this.config.sampleRate)) {
          const errorInfo: ErrorInfo = {
            type: 'http', // 错误类型
            message: `Fetch Error: ${response.status} ${response.statusText}`, // 错误信息
            timestamp: Date.now(), // 时间戳
            url: getCurrentUrl(), // 当前URL
            userAgent: getUserAgent(), // 用户代理
            extra: {
              method: init?.method || 'GET', // 请求方法
              url: typeof input === 'string' ? input : input.toString(), // 请求URL
              status: response.status, // 状态码
              statusText: response.statusText // 状态文本
            }
          };
          this.errorHandler(errorInfo); // 上报错误
        }
        return response; // 返回响应
      } catch (error) {
        if (shouldSample(this.config.sampleRate)) {
          const errorInfo: ErrorInfo = {
            type: 'http', // 错误类型
            message: `Fetch Network Error: ${error}`, // 错误信息
            timestamp: Date.now(), // 时间戳
            url: getCurrentUrl(), // 当前URL
            userAgent: getUserAgent(), // 用户代理
            extra: {
              method: init?.method || 'GET', // 请求方法
              url: typeof input === 'string' ? input : input.toString(), // 请求URL
              error: error // 错误对象
            }
          };
          this.errorHandler(errorInfo); // 上报错误
        }
        throw error; // 继续抛出错误
      }
    };
  }

  // 设置控制台错误处理器
  private setupConsoleErrorHandler(): void {
    const originalConsoleError = console.error; // 保存原始console.error
    const originalConsoleWarn = console.warn; // 保存原始console.warn
    console.error = (...args: any[]) => {
      if (shouldSample(this.config.sampleRate)) {
        const errorInfo: ErrorInfo = {
          type: 'custom', // 错误类型
          message: `Console Error: ${args.map(arg => String(arg)).join(' ')}`, // 错误信息
          timestamp: Date.now(), // 时间戳
          url: getCurrentUrl(), // 当前URL
          userAgent: getUserAgent(), // 用户代理
          extra: {
            consoleArgs: args // 错误参数
          }
        };
        this.errorHandler(errorInfo); // 上报错误
      }
      originalConsoleError.apply(console, args); // 调用原始console.error
    };
    console.warn = (...args: any[]) => {
      if (shouldSample(this.config.sampleRate)) {
        const errorInfo: ErrorInfo = {
          type: 'custom', // 错误类型
          message: `Console Warning: ${args.map(arg => String(arg)).join(' ')}`, // 错误信息
          timestamp: Date.now(), // 时间戳
          url: getCurrentUrl(), // 当前URL
          userAgent: getUserAgent(), // 用户代理
          extra: {
            consoleArgs: args // 错误参数
          }
        };
        this.errorHandler(errorInfo); // 上报错误
      }
      // 因为是箭头函数，所以不能传递this，要传递console对象
      originalConsoleWarn.apply(console, args); // 调用原始console.warn
    };
  }

  // 手动上报错误
  public reportError(error: Error | string, extra?: Record<string, any>): void {
    if (!shouldSample(this.config.sampleRate)) {
      return; // 不采样则返回
    }
    const errorInfo: ErrorInfo = {
      type: 'custom', // 错误类型
      message: typeof error === 'string' ? error : error.message, // 错误信息
      stack: typeof error === 'string' ? '' : formatErrorStack(error), // 堆栈
      timestamp: Date.now(), // 时间戳
      url: getCurrentUrl(), // 当前URL
      userAgent: getUserAgent(), // 用户代理
      extra: extra // 附加信息
    };
    this.errorHandler(errorInfo); // 上报错误
  }

  // 销毁错误监控器，移除所有监听
  public destroy(): void {
    window.removeEventListener('error', this.setupJsErrorHandler); // 移除JS错误监听
    window.removeEventListener('unhandledrejection', this.setupPromiseErrorHandler); // 移除Promise监听
    if (this.config.enableConsole) {
      console.error = console.error; // 恢复原始console.error
      console.warn = console.warn; // 恢复原始console.warn
    }
  }
}
